package br.srv.full.virtual.faces.manager.menu;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.richfaces.component.UIPanelMenuGroup;
import org.richfaces.component.html.HtmlPanelMenu;
import org.richfaces.component.html.HtmlPanelMenuGroup;
import org.richfaces.component.html.HtmlPanelMenuItem;

import br.srv.full.faces.utils.FacesActionUtils;
import br.srv.full.faces.utils.FacesMessageUtils;
import br.srv.full.serviceLocator.ServiceLocator;
import br.srv.full.virtual.engine.auth.AuthManager;
import br.srv.full.virtual.entities.auth.Role;
import br.srv.full.virtual.entities.faces.FacesAction;
import br.srv.full.virtual.entities.faces.FacesOutcome;
import br.srv.full.virtual.entities.menu.MenuGroup;
import br.srv.full.virtual.entities.menu.MenuItem;
import br.srv.full.virtual.entities.resume.UserLogin;
import br.srv.full.virtual.faces.manager.userLogin.LoginEvent;
import br.srv.full.virtual.faces.manager.userLogin.LoginManager;
import br.srv.full.zipper.jsf.event.ZipperEvent;
import br.srv.full.zipper.jsf.event.ZipperEventListener;

public class MenuManager implements ZipperEventListener {

	private final Log log = LogFactory.getLog(MenuManager.class);

	private HtmlPanelMenu panelMenu;

	private LoginManager loginManager;

	private boolean principalPanelMenuEnabled = true;

	private UserLogin lastUserLogin;

	public List<MenuGroup> getPrincipalPanelMenuGroups() {
		return new ArrayList<MenuGroup>();
	}

	public void setPrincipalPanelMenuGroups(List<MenuGroup> principalPanelMenuGroups) {
		// TODO
	}

	public LoginManager getLoginManager() {
		return loginManager;
	}

	public void setLoginManager(LoginManager loginManager) {
		this.loginManager = loginManager;
		loginManager.addEventListener(this);
	}

	public boolean isPrincipalPanelMenuEnabled() {
		return principalPanelMenuEnabled;
	}

	public void setPrincipalPanelMenuEnabled(boolean principalPanelMenuEnabled) {
		this.principalPanelMenuEnabled = principalPanelMenuEnabled;
	}

	public HtmlPanelMenu getPanelMenu() {
		log.info("Criando Panel Menu");

		try {
			if (panelMenu == null) {
				panelMenu = new HtmlPanelMenu();

				panelMenu.setId("PanelMenu");
				panelMenu.setMode("ajax");
				panelMenu.setIconExpandedGroup("disc");
				panelMenu.setIconCollapsedGroup("disc");
				panelMenu.setIconExpandedTopGroup("chevronUp");
				panelMenu.setIconGroupTopPosition("right");
				panelMenu.setIconCollapsedTopGroup("chevronDown");
				// panelMenu .setIconCollapsedTopPosition("right");
				panelMenu.setDisabled(!isPrincipalPanelMenuEnabled()); 
			}
			UserLogin l_userLogin = getLoginManager().getUserLogin();
			if (!l_userLogin.equals(lastUserLogin) || needUpdateMenu) {
				populatePanelMenu(panelMenu.getChildren());
				lastUserLogin = l_userLogin;
			}
		} catch (Exception e) {
			log.warn("Problemas ao montar o menu!!!!", e);
			FacesMessageUtils.addMessage("PanelMenu", FacesMessage.SEVERITY_FATAL, "Menu Não pode ser montado", e
					.getLocalizedMessage());
			HtmlPanelMenu oldMenu = panelMenu;
			panelMenu = null;
			return oldMenu;
		}
		return panelMenu;
	}

	MapaAntiDuplicidade l_mapaAnteDuplicidade = new MapaAntiDuplicidade();

	private boolean needUpdateMenu;

	@PostConstruct
	public void PostConstruct() {
		log.info("PostConstruct");
	}

	/**
	 * @param p_children
	 */
	@SuppressWarnings("unchecked")
	private synchronized void populatePanelMenu(List<UIComponent> p_children) {
		log.info("Limpando PanelMenu!");
		p_children.clear();
		l_mapaAnteDuplicidade.clear();

		log.info("Populando PanelMenu!");

		// MenuItem l_item;
		HtmlPanelMenuItem l_menuItem;

		AuthManager l_authManager = getAuthManager();

		l_menuItem = createPanelMenuItem(l_authManager, "start");
		ensureNoDuplicate(p_children, l_menuItem);

		UserLogin l_userLogin = updateUserRoles();

		List<Role> l_roles = null;

		List<MenuGroup> l_find;
		if (l_userLogin != null) {
			log.info("Buscando Grupos de Menu conforme os Papeis desempenhado pelo Usuário: " + l_userLogin);
			l_roles = l_userLogin.getRoles();
			if (l_roles == null || l_roles.isEmpty()) {
				log.info("Usuário Não desempenha nenhum papel!");
				l_find = l_authManager.getMenus(MenuGroup.class);

			} else {
				Object[][] l_arrayRoles = new Object[][] { l_roles.toArray(new Role[] {}) };
				log.info("Papeis desempenhado pelo Usuário: " + l_roles);
				try {
					l_find = l_authManager.getMenus(MenuGroup.class, l_roles);
				} catch (Exception e) {
					log.warn("Problemas ao pesquisar os Roles: " + l_arrayRoles);
					l_find = null;
				}
			}
		} else {
			log.info("Usuário não foi definido entáo procurando por menus "
					+ "de usuários que não desempenha nenhum papel!");
			l_find = l_authManager.getMenus(MenuGroup.class);
		}
		log.info("Usando Os Grupos: " + l_find);

		MenuGroup l_group = null;
		HtmlPanelMenuGroup l_hpmg = null;
		try {
			// String l_string = "register";
			// l_group = (MenuGroup) l_authManager.getMenu(MenuGroup.class,
			// l_string);
			// l_hpmg = createPanelMenuGroup(l_group);
			// ensureNoDuplicate(p_children, l_hpmg);
			// FacesManagedBean l_facesManagedBean = new
			// FacesManagedBean("loginManager");

			// MenuItem l_itemCadastrar = null;
			// if (l_userLogin != null && l_userLogin.isLogged()) {
			// // obtem o item de menu que permite ao usu[ario sair do sistema
			// l_item = (MenuItem) l_authManager.getMenu(MenuItem.class,
			// "logoff");
			// // obtem o item de menu que permite ao usuário editar seus dados
			// l_itemCadastrar = (MenuItem)
			// l_authManager.getMenu(MenuItem.class, "recordsMaintenance");
			// } else {
			// // obtem o item de menu que permite ao usu[ario se logar no
			// // sistema
			// l_item = (MenuItem) l_authManager.getMenu(MenuItem.class,
			// "login");
			//
			// // Obtem o item de menu para novos registros
			// l_itemCadastrar = (MenuItem)
			// l_authManager.getMenu(MenuItem.class, "newRecord");
			// }

			// l_menuItem = createPanelMenuItem(l_item);
			// if (l_menuItem != null)
			// ensureNoDuplicate(l_hpmg, l_menuItem);

			// if (l_itemCadastrar != null) {
			// l_menuItem = createPanelMenuItem(l_itemCadastrar);
			// if (l_menuItem != null)
			// ensureNoDuplicate(l_hpmg, l_menuItem);
			// }
		} catch (Throwable e) {
			// nâo monta o menu de registro.
			log.warn("Problema ao mantar o menu de registro padráo", e);
		}

		List<UIComponent> l_hpmgChildren = null;
		// if (l_find instanceof Collection) {
		Collection<MenuGroup> l_groups = (List<MenuGroup>) l_find;

		for (MenuGroup l_groupTmp : l_groups) {
			if (l_groupTmp == null)
				continue;
			log.info("encontrado o Grupo: " + l_groupTmp);

			if (l_groupTmp.isRootGroup()) {
				log.info("Este grupo 'e a base do menu!");
				l_hpmgChildren = p_children;
			} else // if (!l_groupTmp.equals(l_group))
			{
				l_hpmg = createPanelMenuGroup(l_groupTmp);
				ensureNoDuplicate(p_children, l_hpmg);
				l_hpmgChildren = l_hpmg.getChildren();
				// } else {
				// log.info("Este grupo é igual ao grupo " + l_group);
				// l_groupTmp = l_group;
				// l_hpmgChildren = l_hpmg.getChildren();
			}

			log.info("Montando o Grupo: " + l_groupTmp);
			populatePanelMenuGroup(l_authManager, l_userLogin, l_hpmg, l_groupTmp, l_hpmgChildren, l_roles);
		}
		// informa que o menu já foi atualizado.
		needUpdateMenu = false;
	}

	/**
	 * @return
	 */
	private UserLogin updateUserRoles() {
		return loginManager.refreshUserLogin();
	}

	/**
	 * @param p_manager
	 * @return
	 */
	private HtmlPanelMenuItem createPanelMenuItem(AuthManager p_manager, String p_name) {
		MenuItem l_item;
		HtmlPanelMenuItem l_menuItem = null;

		l_item = (MenuItem) p_manager.getMenu(MenuItem.class, p_name);
		if (l_item != null)
			l_menuItem = createPanelMenuItem(l_item);
		return l_menuItem;
	}

	/**
	 * @param p_group
	 * @return
	 */
	private HtmlPanelMenuGroup createPanelMenuGroup(MenuGroup p_group) {
		HtmlPanelMenuGroup l_hpmg = new HtmlPanelMenuGroup();

		l_hpmg.setLabel(p_group.getLabel());
		l_hpmg.setName(p_group.getName());
		l_hpmg.setId(p_group.getName());
		l_hpmg.setDisabled(p_group.isDisabled());
		l_hpmg.setExpanded(p_group.isExpanded());


		l_hpmg.setAjaxSingle(true);
		l_hpmg.setLimitToList(true);


		return l_hpmg;
	}

	/**
	 * @param p_group
	 * @param p_rules
	 * @param p_hpmg
	 */
	private void populatePanelMenuGroup(AuthManager l_authManager, UserLogin l_userLogin, UIPanelMenuGroup p_pgroup,
			MenuGroup p_group, List<UIComponent> l_children, List<Role> p_rules) {
		Object l_find;

		try {
			l_find = l_authManager.getMenus(MenuItem.class, p_group, p_rules);
		} catch (Exception e) {
			log.warn("Estamos Com problema na recuperação dos menus por grupo e roles!", e);
			return;
		}

		log.info("Populando Menu Group " + p_group);

		if (l_find instanceof Collection) {

			@SuppressWarnings("unchecked")
			Collection<MenuItem> l_itens = (Collection<MenuItem>) l_find;

			for (MenuItem l_item : l_itens) {
				Boolean l_onlyForNoLogged = l_item.getOnlyForNoLogged();
				Boolean l_logged = l_userLogin.isLogged();
				log.info("usuário" + l_userLogin + " logado: " + l_logged);
				if (l_item != null)
					log.info("Menu " + l_item.getName() + " somente para não logados: " + l_onlyForNoLogged);
				if (l_item == null
				// TODO: ISSUE verificar esta logica para menus somente para
						// usuários logados!
						|| (l_onlyForNoLogged && l_logged))
					continue;
				HtmlPanelMenuItem l_hpmi = createPanelMenuItem(l_item);
				if (l_hpmi != null)
					ensureNoDuplicate(l_children, l_hpmi);

			}
		} else if (l_find != null) {

			log.info(l_find);
			MenuItem _menuItem = (MenuItem) l_find;
			HtmlPanelMenuItem l_hpmi = createPanelMenuItem(_menuItem);
			if (l_hpmi != null)
				ensureNoDuplicate(l_children, l_hpmi);
		}
	}

	/**
	 * @param l_group
	 * @return
	 */
	private HtmlPanelMenuItem createPanelMenuItem(MenuItem p_item) {
		HtmlPanelMenuItem l_hpmi = new HtmlPanelMenuItem();
		l_hpmi.setLabel(p_item.getLabel());
		l_hpmi.setName(p_item.getName());
		l_hpmi.setId(p_item.getName());
		l_hpmi.setDisabled(p_item.isDisabled());

		l_hpmi.setAjaxSingle(true);
		l_hpmi.setLimitToList(true);

		FacesAction l_facesAction = p_item.getFacesAction();
		log.info("Faces Action: " + l_facesAction);

		if (l_facesAction == null) {
			l_facesAction = new FacesOutcome(p_item.getName());
			p_item.setFacesAction(l_facesAction);
		}

		l_hpmi.setValue(l_facesAction.toString());
		l_hpmi.setActionExpression(FacesActionUtils.createActionExpression(p_item));

		return l_hpmi;
	}

	public void setPanelMenu(HtmlPanelMenu p_panelMenu) {
		log.info("Setando o PanelMenu no BackBean!");
		// this.panelMenu = p_panelMenu;
	}

	// /**
	// * @param p_string
	// */
	// public FacesOutcome getFacesOutcomeToMenu(String p_string) {
	// if (p_string == null || p_string.trim().isEmpty())
	// throw new MenuManagerException("Nome do Menu náo pode ser nulo");
	//
	// AuthManager l_authManager = getAuthManager();
	// MenuItem l_menu = l_authManager.getMenu(MenuItem.class, p_string);
	//
	// FacesAction l_outcome = l_menu.getFacesAction();
	// if (l_outcome instanceof FacesOutcome) {
	// return (FacesOutcome) l_outcome;
	// }
	// return null;
	// }

	/**
	 * @param p_hpmg
	 * @param p_menuItem
	 */
	private void ensureNoDuplicate(HtmlPanelMenuGroup p_hpmg, HtmlPanelMenuItem p_menuItem) {
		if (l_mapaAnteDuplicidade.add(p_hpmg, p_menuItem)) {
			log.info(p_menuItem.getName() + " Não foi ainda adicionado!");
			p_hpmg.getChildren().add(p_menuItem);
		} else {
			log.info(p_menuItem.getName() + " Já existe, e não será adicionado!");
		}
	}

	/**
	 * @param p_children
	 * @param p_hpmg
	 */
	private void ensureNoDuplicate(List<UIComponent> p_children, HtmlPanelMenuGroup p_hpmg) {
		if (l_mapaAnteDuplicidade.add(p_hpmg)) {
			log.info(p_hpmg.getName() + " Está sendo adicionado!");
			p_children.add(p_hpmg);
		} else {
			log.info(p_hpmg.getName() + " Já existe, e não será adicionado!");
		}
	}

	/**
	 * @param p_children
	 * @param p_hpmi
	 */
	private void ensureNoDuplicate(List<UIComponent> p_children, HtmlPanelMenuItem p_hpmi) {
		if (l_mapaAnteDuplicidade.add(p_hpmi)) {
			log.info(p_hpmi.getName() + " Está sendo adicionado!");
			p_children.add(p_hpmi);
		} else {
			log.info(p_hpmi.getName() + " Já existe, e não será adicionado!");
		}

	}

	/**
	 * @return
	 */
	private AuthManager getAuthManager() {
		return ServiceLocator.getService(AuthManager.class, "remote");
	}

	public void updateMenu() {
		log.info("Limpando menus, para recarga");
		needUpdateMenu = true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.srv.full.virtual.faces.manager.LoginEventListener#processEvent(br.srv.full.virtual.faces.manager.LoginEvent)
	 */
	@Override
	public void processEvent(ZipperEvent p_loginEvent) {
		if (p_loginEvent.isEvent(LoginEvent.USER_LOGIN) && p_loginEvent.isEvent(LoginEvent.USER_LOGOFF)
				&& p_loginEvent.isEvent(LoginEvent.USER_ADD_ROLES)
				&& p_loginEvent.isEvent(LoginEvent.USER_REMOVE_ROLES)) {
			updateMenu();
		}
	}
}
